﻿#include "des.h"

using std::string;

//! 初始置换表
static const unsigned char IP_TB[] =
  {
  //0     1     2     3     4     5     6     7
  0x3a, 0x32, 0x2a, 0x22, 0x1a, 0x12, 0x0a, 0x02, //0
  0x3c, 0x34, 0x2c, 0x24, 0x1c, 0x14, 0x0c, 0x04, //1
  0x3e, 0x36, 0x2e, 0x26, 0x1e, 0x16, 0x0e, 0x06, //2
  0x40, 0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08, //3
  0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01, //4
  0x3b, 0x33, 0x2b, 0x23, 0x1b, 0x13, 0x0b, 0x03, //5
  0x3d, 0x35, 0x2d, 0x25, 0x1d, 0x15, 0x0d, 0x05, //6
  0x3f, 0x37, 0x2f, 0x27, 0x1f, 0x17, 0x0f, 0x07, //7
  };

//! 逆置换表
static const unsigned char RP_TB[] =
  {
  //0     1     2     3     4     5     6     7
  0x28, 0x08, 0x30, 0x10, 0x38, 0x18, 0x40, 0x20, //0
  0x27, 0x07, 0x2f, 0x0f, 0x37, 0x17, 0x3f, 0x1f, //1
  0x26, 0x06, 0x2e, 0x0e, 0x36, 0x16, 0x3e, 0x1e, //2
  0x25, 0x05, 0x2d, 0x0d, 0x35, 0x15, 0x3d, 0x1d, //3
  0x24, 0x04, 0x2c, 0x0c, 0x34, 0x14, 0x3c, 0x1c, //4
  0x23, 0x03, 0x2b, 0x0b, 0x33, 0x13, 0x3b, 0x1b, //5
  0x22, 0x02, 0x2a, 0x0a, 0x32, 0x12, 0x3a, 0x1a, //6
  0x21, 0x01, 0x29, 0x09, 0x31, 0x11, 0x39, 0x19, //7
  };

//! 密码置换表1
static const unsigned char PC1_TB[] =
  {
  //0     1     2     3     4     5     6     7
  0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01,  //0
  0x3a, 0x32, 0x2a, 0x22, 0x1a, 0x12, 0x0a, 0x02,  //1
  0x3b, 0x33, 0x2b, 0x23, 0x1b, 0x13, 0x0b, 0x03,  //2
  0x3c, 0x34, 0x2c, 0x24, 0x3f, 0x37, 0x2f, 0x27,  //3
  0x1f, 0x17, 0x0f, 0x07, 0x3e, 0x36, 0x2e, 0x26,  //4
  0x1e, 0x16, 0x0e, 0x06, 0x3d, 0x35, 0x2d, 0x25,  //5
  0x1d, 0x15, 0x0d, 0x05, 0x1c, 0x14, 0x0c, 0x04,  //6
  };

//! 密码置换表2
static const unsigned char PC2_TB[] =
  {
  //0     1     2     3     4     5     6     7
  0x0e, 0x11, 0x0b, 0x18, 0x01, 0x05, 0x03, 0x1c,  //0
  0x0f, 0x06, 0x15, 0x0a, 0x17, 0x13, 0x0c, 0x04,  //1
  0x1a, 0x08, 0x10, 0x07, 0x1b, 0x14, 0x0d, 0x02,  //2
  0x29, 0x34, 0x1f, 0x25, 0x2f, 0x37, 0x1e, 0x28,  //3
  0x33, 0x2d, 0x21, 0x30, 0x2c, 0x31, 0x27, 0x38,  //4
  0x22, 0x35, 0x2e, 0x2a, 0x32, 0x24, 0x1d, 0x20,  //5
  };

//! 循环左移表
static const unsigned char Shift_TB[] =
  {
  //0     1     2     3     4     5     6     7
  0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,  //0
  0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01,  //1
  0
  };

//! 放大换位表
static const unsigned char EO_TB[] =
  {
  //0     1     2     3     4     5     6     7
  0x20, 0x01, 0x02, 0x03, 0x04, 0x05, 0x04, 0x05,  //0
  0x06, 0x07, 0x08, 0x09, 0x08, 0x09, 0x0a, 0x0b,  //1
  0x0c, 0x0d, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,  //2
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x14, 0x15,  //3
  0x16, 0x17, 0x18, 0x19, 0x18, 0x19, 0x1a, 0x1b,  //4
  0x1c, 0x1d, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x01,  //5
  };

//! 单纯换位表
static const unsigned char PP_TB[] =
  {
  //0     1     2     3     4     5     6     7
  0x10, 0x07, 0x14, 0x15, 0x1d, 0x0c, 0x1c, 0x11, //0
  0x01, 0x0f, 0x17, 0x1a, 0x05, 0x12, 0x1f, 0x0a, //1
  0x02, 0x08, 0x18, 0x0e, 0x20, 0x1b, 0x03, 0x09, //2
  0x13, 0x0d, 0x1e, 0x06, 0x16, 0x0b, 0x04, 0x19, //3
  };

//! SBox
static const unsigned char sBox[8][4][16] =
  {
  //0     1     2     3     4     5     6     7
  0x0e, 0x04, 0x0d, 0x01, 0x02, 0x0f, 0x0b, 0x08, //0
  0x03, 0x0a, 0x06, 0x0c, 0x05, 0x09, 0x00, 0x07, //1
  0x00, 0x0f, 0x07, 0x04, 0x0e, 0x02, 0x0d, 0x01, //2
  0x0a, 0x06, 0x0c, 0x0b, 0x09, 0x05, 0x03, 0x08, //3
  0x04, 0x01, 0x0e, 0x08, 0x0d, 0x06, 0x02, 0x0b, //4
  0x0f, 0x0c, 0x09, 0x07, 0x03, 0x0a, 0x05, 0x00, //5
  0x0f, 0x0c, 0x08, 0x02, 0x04, 0x09, 0x01, 0x07, //6
  0x05, 0x0b, 0x03, 0x0e, 0x0a, 0x00, 0x06, 0x0d, //7

  0x0f, 0x01, 0x08, 0x0e, 0x06, 0x0b, 0x03, 0x04, //0
  0x09, 0x07, 0x02, 0x0d, 0x0c, 0x00, 0x05, 0x0a, //1
  0x03, 0x0d, 0x04, 0x07, 0x0f, 0x02, 0x08, 0x0e, //2
  0x0c, 0x00, 0x01, 0x0a, 0x06, 0x09, 0x0b, 0x05, //3
  0x00, 0x0e, 0x07, 0x0b, 0x0a, 0x04, 0x0d, 0x01, //4
  0x05, 0x08, 0x0c, 0x06, 0x09, 0x03, 0x02, 0x0f, //5
  0x0d, 0x08, 0x0a, 0x01, 0x03, 0x0f, 0x04, 0x02, //6
  0x0b, 0x06, 0x07, 0x0c, 0x00, 0x05, 0x0e, 0x09, //7

  0x0a, 0x00, 0x09, 0x0e, 0x06, 0x03, 0x0f, 0x05, //0
  0x01, 0x0d, 0x0c, 0x07, 0x0b, 0x04, 0x02, 0x08, //1
  0x0d, 0x07, 0x00, 0x09, 0x03, 0x04, 0x06, 0x0a, //2
  0x02, 0x08, 0x05, 0x0e, 0x0c, 0x0b, 0x0f, 0x01, //3
  0x0d, 0x06, 0x04, 0x09, 0x08, 0x0f, 0x03, 0x00, //4
  0x0b, 0x01, 0x02, 0x0c, 0x05, 0x0a, 0x0e, 0x07, //5
  0x01, 0x0a, 0x0d, 0x00, 0x06, 0x09, 0x08, 0x07, //6
  0x04, 0x0f, 0x0e, 0x03, 0x0b, 0x05, 0x02, 0x0c, //7

  0x07, 0x0d, 0x0e, 0x03, 0x00, 0x06, 0x09, 0x0a, //0
  0x01, 0x02, 0x08, 0x05, 0x0b, 0x0c, 0x04, 0x0f, //1
  0x0d, 0x08, 0x0b, 0x05, 0x06, 0x0f, 0x00, 0x03, //2
  0x04, 0x07, 0x02, 0x0c, 0x01, 0x0a, 0x0e, 0x09, //3
  0x0a, 0x06, 0x09, 0x00, 0x0c, 0x0b, 0x07, 0x0d, //4
  0x0f, 0x01, 0x03, 0x0e, 0x05, 0x02, 0x08, 0x04, //5
  0x03, 0x0f, 0x00, 0x06, 0x0a, 0x01, 0x0d, 0x08, //6
  0x09, 0x04, 0x05, 0x0b, 0x0c, 0x07, 0x02, 0x0e, //7

  0x02, 0x0c, 0x04, 0x01, 0x07, 0x0a, 0x0b, 0x06, //0
  0x08, 0x05, 0x03, 0x0f, 0x0d, 0x00, 0x0e, 0x09, //1
  0x0e, 0x0b, 0x02, 0x0c, 0x04, 0x07, 0x0d, 0x01, //2
  0x05, 0x00, 0x0f, 0x0a, 0x03, 0x09, 0x08, 0x06, //3
  0x04, 0x02, 0x01, 0x0b, 0x0a, 0x0d, 0x07, 0x08, //4
  0x0f, 0x09, 0x0c, 0x05, 0x06, 0x03, 0x00, 0x0e, //5
  0x0b, 0x08, 0x0c, 0x07, 0x01, 0x0e, 0x02, 0x0d, //6
  0x06, 0x0f, 0x00, 0x09, 0x0a, 0x04, 0x05, 0x03, //7

  0x0c, 0x01, 0x0a, 0x0f, 0x09, 0x02, 0x06, 0x08, //0
  0x00, 0x0d, 0x03, 0x04, 0x0e, 0x07, 0x05, 0x0b, //1
  0x0a, 0x0f, 0x04, 0x02, 0x07, 0x0c, 0x09, 0x05, //2
  0x06, 0x01, 0x0d, 0x0e, 0x00, 0x0b, 0x03, 0x08, //3
  0x09, 0x0e, 0x0f, 0x05, 0x02, 0x08, 0x0c, 0x03, //4
  0x07, 0x00, 0x04, 0x0a, 0x01, 0x0d, 0x0b, 0x06, //5
  0x04, 0x03, 0x02, 0x0c, 0x09, 0x05, 0x0f, 0x0a, //6
  0x0b, 0x0e, 0x01, 0x07, 0x06, 0x00, 0x08, 0x0d, //7

  0x04, 0x0b, 0x02, 0x0e, 0x0f, 0x00, 0x08, 0x0d, //0
  0x03, 0x0c, 0x09, 0x07, 0x05, 0x0a, 0x06, 0x01, //1
  0x0d, 0x00, 0x0b, 0x07, 0x04, 0x09, 0x01, 0x0a, //2
  0x0e, 0x03, 0x05, 0x0c, 0x02, 0x0f, 0x08, 0x06, //3
  0x01, 0x04, 0x0b, 0x0d, 0x0c, 0x03, 0x07, 0x0e, //4
  0x0a, 0x0f, 0x06, 0x08, 0x00, 0x05, 0x09, 0x02, //5
  0x06, 0x0b, 0x0d, 0x08, 0x01, 0x04, 0x0a, 0x07, //6
  0x09, 0x05, 0x00, 0x0f, 0x0e, 0x02, 0x03, 0x0c, //7

  0x0d, 0x02, 0x08, 0x04, 0x06, 0x0f, 0x0b, 0x01, //0
  0x0a, 0x09, 0x03, 0x0e, 0x05, 0x00, 0x0c, 0x07, //1
  0x01, 0x0f, 0x0d, 0x08, 0x0a, 0x03, 0x07, 0x04, //2
  0x0c, 0x05, 0x06, 0x0b, 0x00, 0x0e, 0x09, 0x02, //3
  0x07, 0x0b, 0x04, 0x01, 0x09, 0x0c, 0x0e, 0x02, //4
  0x00, 0x06, 0x0a, 0x0d, 0x0f, 0x03, 0x05, 0x08, //5
  0x02, 0x01, 0x0e, 0x07, 0x04, 0x0a, 0x08, 0x0d, //6
  0x0f, 0x0c, 0x09, 0x00, 0x03, 0x05, 0x06, 0x0b, //7
  };

static void movram(const unsigned char* source,
                   unsigned char* target,
                   const size_t length)
  {
  for(size_t i = 0; i < length; ++i)
    {
    target[i] = source[i];
    }
  }

static void doxor(unsigned char* sourceaddr,
                  const unsigned char* targetaddr,
                  const size_t length)
  {
  for(size_t i = 0; i < length; ++i)
    {
    sourceaddr[i] ^= targetaddr[i];
    }
  }

static void setbit(unsigned char* dataddr,
                   const size_t pos,
                   const unsigned char b)
  {
  size_t byte_count = (pos - 1) / 8;
  size_t bit_count = 7 - ((pos - 1) % 8);
  unsigned char temp = 1 << bit_count;
  if(b)
    {
    dataddr[byte_count] |= temp;
    }
  else
    {
    temp = ~temp;
    dataddr[byte_count] &= temp;
    }
  }

static unsigned char getbit(const unsigned char* dataddr,
                            const size_t pos)
  {
  size_t byte_count = (pos - 1) / 8;
  size_t bit_count = 7 - ((pos - 1) % 8);
  unsigned char temp = 1 << bit_count;
  if(dataddr[byte_count] & temp)
    {
    return(1);
    }
  else
    {
    return(0);
    }
  }

static void selectbits(const unsigned char* source,
                       const unsigned char* table,
                       unsigned char* target,
                       const size_t count)
  {
  for(size_t i = 0; i < count; ++i)
    {
    setbit(target, i + 1, getbit(source, table[i]));
    }
  }

static void shlc(unsigned char* data_p)
  {
  unsigned char b = getbit(data_p, 1);

  for(size_t i = 0; i < 7; ++i)
    {
    data_p[i] <<= 1;
    if(i != 6)
      {
      setbit(&data_p[i], 8, getbit(&data_p[i + 1], 1));
      }
    }

  setbit(data_p, 56, getbit(data_p, 28));
  setbit(data_p, 28, b);
  }

static void shrc(unsigned char* data_p)
  {
  unsigned char b = getbit(data_p, 56);

  for(intptr_t i = 6; i >= 0; i--)
    {
    data_p[i] >>= 1;
    if(i != 0)
      {
      setbit(&data_p[i], 1, getbit(&data_p[i - 1], 8));
      }
    }

  setbit(data_p, 1, getbit(data_p, 29));
  setbit(data_p, 29, b);
  }

static void strans(const unsigned char* index,
                   unsigned char* target)
  {
  for(size_t i = 0; i < 4; ++i)
    {
    unsigned char row = 0;
    unsigned char line = 0;
    unsigned char t = 0;
    setbit(&line, 7, getbit(index, i * 12 + 1));
    setbit(&line, 8, getbit(index, i * 12 + 6));

    for(size_t j = 2; j < 6; ++j)
      {
      setbit(&row, 3 + j, getbit(index, i * 12 + j));
      }

    t = sBox[i * 2][line][row];
    t <<= 4;
    line = row = 0;
    setbit(&line, 7, getbit(index, i * 12 + 7));
    setbit(&line, 8, getbit(index, i * 12 + 12));

    for(size_t j = 2; j < 6; ++j)
      {
      setbit(&row, 3 + j, getbit(index, i * 12 + 6 + j));
      }

    t |= sBox[i * 2 + 1][line][row];
    target[i] = t;
    }
  }

static void des(unsigned char *data_p,
                const unsigned char* key_p,
                int type)
  {
  unsigned char tempbuf[12];
  unsigned char key[7];
  void(*f)(unsigned char* data_p);

  selectbits(data_p, IP_TB, tempbuf, 64);
  movram(tempbuf, data_p, 8);
  selectbits(key_p, PC1_TB, key, 56);

  for(size_t i = 0; i < 16; ++i)
    {
    selectbits(data_p + 4, EO_TB, tempbuf, 48);

    size_t count;
    if(type == 1)
      {
      f = shlc;
      count = i;
      }
    else
      {
      count = 16 - i;
      f = shrc;
      }

    for(size_t j = 0; j < Shift_TB[count]; ++j)
      {
      f(key);
      }

    selectbits(key, PC2_TB, tempbuf + 6, 48);
    doxor(tempbuf, tempbuf + 6, 6);
    strans(tempbuf, tempbuf + 6);
    selectbits(tempbuf + 6, PP_TB, tempbuf, 32);
    doxor(tempbuf, data_p, 4);

    if(i < 15)
      {
      movram(data_p + 4, data_p, 4);
      movram(tempbuf, data_p + 4, 4);
      }
    }

  movram(tempbuf, data_p, 4);
  selectbits(data_p, RP_TB, tempbuf, 64);
  movram(tempbuf, data_p, 8);
  }

string DesEncrypt(const void*     encrypt_data,
                  const size_t    encrypt_data_size,
                  const char*     encrypt_key)
  {
  string rets;
  rets.append((const char*)encrypt_data, encrypt_data_size);

  if(encrypt_data_size % 8 != 0)
    {
    const unsigned char ch = 8 - (encrypt_data_size % 8);
    rets.append(ch, ch);
    }

  const unsigned char* key_p = (const unsigned char*)encrypt_key;
  unsigned char* data_p = (unsigned char*)rets.c_str();

  for(size_t i = 0; i < rets.size(); i += 8)
    {
    des(&data_p[i], key_p, 1);
    }
  return rets;
  }

string DesDecrypt(const void*     decrypt_data,
                  const size_t    decrypt_data_size,
                  const char*     decrypt_key)
  {
  if(decrypt_data_size % 8 != 0)
    {
    return string();
    }

  string rets;
  rets.append((const char*)decrypt_data, decrypt_data_size);

  const unsigned char* key_p = (const unsigned char*)decrypt_key;
  unsigned char* data_p = (unsigned char*)rets.c_str();

  for(size_t i = 0; i < decrypt_data_size; i += 8)
    {
    des(&data_p[i], key_p, 0);
    }

  const unsigned char ch = *rets.rbegin();
  if(ch > 0x7)  return rets;

  if((size_t)ch > rets.size())  return string();

  bool ispadding = true;
  for(auto it = rets.rbegin(); it != (rets.rbegin() + ch); ++it)
    {
    if(*it != ch)
      {
      ispadding = false;
      break;
      }
    }
  if(ispadding) rets.resize(rets.size() - ch);
  return rets;
  }